﻿// ReSharper disable once CheckNamespace
namespace Fluent;

using System;
using System.Collections.Specialized;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Threading;
using Fluent.Extensions;
using Fluent.Localization;

/// <summary>
/// Represents ribbon status bar
/// </summary>
public class StatusBar : System.Windows.Controls.Primitives.StatusBar
{
    #region Fields

    // Context menu
    private readonly ContextMenu contextMenu = new();

    private bool waitingForItemContainerGenerator;

    #endregion

    #region Properties

    private object? currentItem;

    #endregion

    #region Constructors

    /// <summary>
    /// Static constructor
    /// </summary>
    static StatusBar()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(StatusBar), new FrameworkPropertyMetadata(typeof(StatusBar)));
    }

    /// <summary>
    /// Default constructor
    /// </summary>
    public StatusBar()
    {
        this.RecreateMenu();
        this.ContextMenu = this.contextMenu;

        this.Loaded += this.OnLoaded;
        this.Unloaded += this.OnUnloaded;

        this.ItemContainerGenerator.StatusChanged += this.HandleItemContainerGeneratorStatusChanged;
    }

    private void OnUnloaded(object sender, RoutedEventArgs e)
    {
    }

    private void OnLoaded(object sender, RoutedEventArgs e)
    {
    }

    #endregion

    #region Overrides

    /// <inheritdoc />
    protected override DependencyObject GetContainerForItemOverride()
    {
        var item = this.currentItem;
        this.currentItem = null;

        if (this.UsesItemContainerTemplate
            && item is not null)
        {
            var dataTemplate = this.ItemContainerTemplateSelector.SelectTemplate(item, this);
            if (dataTemplate is not null)
            {
                var dataTemplateContent = (object)dataTemplate.LoadContent();
                if (dataTemplateContent is StatusBarItem or Separator)
                {
                    return (DependencyObject)dataTemplateContent;
                }

                throw new InvalidOperationException("Invalid ItemContainer");
            }
        }

        return new StatusBarItem();
    }

    /// <inheritdoc />
    protected override bool IsItemItsOwnContainerOverride(object item)
    {
        var isItemItsOwnContainerOverride = item is StatusBarItem || item is Separator;

        if (isItemItsOwnContainerOverride == false)
        {
            this.currentItem = item;
        }

        return isItemItsOwnContainerOverride;
    }

    private void HandleItemContainerGeneratorStatusChanged(object? sender, EventArgs e)
    {
        if (this.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
        {
            return;
        }

        this.RunInDispatcherAsync(this.RecreateMenu, DispatcherPriority.Loaded);
    }

    /// <inheritdoc />
    protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
    {
        base.OnItemsChanged(e);

        if (this.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated
            || this.waitingForItemContainerGenerator)
        {
            this.waitingForItemContainerGenerator = true;
            return;
        }

        switch (e.Action)
        {
            case NotifyCollectionChangedAction.Add:
            {
                foreach (var newItem in e.NewItems.NullSafe())
                {
                    var container = this.ItemContainerGenerator.ContainerFromItem(newItem);
                    var containerIndex = this.ItemContainerGenerator.IndexFromContainer(container);

                    if (container is StatusBarItem item)
                    {
                        item.Checked += this.OnItemChecked;
                        item.Unchecked += this.OnItemUnchecked;
                        this.contextMenu.Items.Insert(containerIndex, new StatusBarMenuItem(item));
                    }
                    else
                    {
                        this.contextMenu.Items.Insert(containerIndex, new Separator());
                    }
                }

                break;
            }

            case NotifyCollectionChangedAction.Move:
            {
                for (var i = 0; i < e.NewItems?.Count; i++)
                {
                    var menuItem = this.contextMenu.Items[e.OldStartingIndex + 1];
                    this.contextMenu.Items.Remove(e.OldStartingIndex + 1);
                    this.contextMenu.Items.Insert(e.NewStartingIndex + i + 1, menuItem);
                }

                break;
            }

            case NotifyCollectionChangedAction.Remove:
            {
                for (var i = 0; i < e.OldItems?.Count; i++)
                {
                    if (this.contextMenu.Items[e.OldStartingIndex + 1] is StatusBarMenuItem menuItem)
                    {
                        if (menuItem.StatusBarItem is not null)
                        {
                            menuItem.StatusBarItem.Checked -= this.OnItemChecked;
                            menuItem.StatusBarItem.Unchecked -= this.OnItemUnchecked;
                        }
                    }

                    this.contextMenu.Items.RemoveAt(e.OldStartingIndex + 1);
                }

                break;
            }

            case NotifyCollectionChangedAction.Replace:
            {
                for (var i = 0; i < e.OldItems?.Count; i++)
                {
                    if (this.contextMenu.Items[e.OldStartingIndex + 1] is StatusBarMenuItem menuItem)
                    {
                        if (menuItem.StatusBarItem is not null)
                        {
                            menuItem.StatusBarItem.Checked -= this.OnItemChecked;
                            menuItem.StatusBarItem.Unchecked -= this.OnItemUnchecked;
                        }
                    }

                    this.contextMenu.Items.RemoveAt(e.OldStartingIndex + 1);
                }

                for (var i = 0; i < e.NewItems?.Count; i++)
                {
                    if (this.ItemContainerGenerator.ContainerFromItem(e.NewItems![i]) is StatusBarItem item)
                    {
                        item.Checked += this.OnItemChecked;
                        item.Unchecked += this.OnItemUnchecked;
                        this.contextMenu.Items.Insert(e.NewStartingIndex + i + 1, new StatusBarMenuItem(item));
                    }
                    else
                    {
                        this.contextMenu.Items.Insert(e.NewStartingIndex + i + 1, new Separator());
                    }
                }

                break;
            }

            case NotifyCollectionChangedAction.Reset:
            {
                this.RecreateMenu();
                break;
            }
        }
    }

    private void OnItemUnchecked(object sender, RoutedEventArgs e)
    {
        this.UpdateSeparartorsVisibility();
    }

    private void OnItemChecked(object sender, RoutedEventArgs e)
    {
        this.UpdateSeparartorsVisibility();
    }

    #endregion

    #region Private Methods

    // Creates menu
    private void RecreateMenu()
    {
        this.contextMenu.Items.Clear();

        // Adding header separator
        this.contextMenu.Items.Add(new GroupSeparatorMenuItem());
        var target = this.contextMenu.Items[0] as FrameworkElement;

        if (target is not null)
        {
            RibbonControl.Bind(RibbonLocalization.Current.Localization, target, nameof(RibbonLocalizationBase.CustomizeStatusBar), HeaderedItemsControl.HeaderProperty, BindingMode.OneWay);
        }

        for (var i = 0; i < this.Items.Count; i++)
        {
            if (this.ItemContainerGenerator.ContainerFromItem(this.Items[i]) is StatusBarItem item)
            {
                // Prevent double event handler
                item.Checked -= this.OnItemChecked;
                item.Unchecked -= this.OnItemUnchecked;

                item.Checked += this.OnItemChecked;
                item.Unchecked += this.OnItemUnchecked;
                this.contextMenu.Items.Add(new StatusBarMenuItem(item));
            }
            else
            {
                this.contextMenu.Items.Add(new Separator());
            }
        }

        this.UpdateSeparartorsVisibility();

        this.waitingForItemContainerGenerator = false;
    }

    // Updates separators visibility, to not duplicate
    private void UpdateSeparartorsVisibility()
    {
        var isPrevSeparator = false;
        var isFirstVsible = true;

        foreach (var item in this.Items)
        {
            var containerFromItem = this.ItemContainerGenerator.ContainerFromItem(item);

            if (containerFromItem is Separator separator)
            {
                if (isPrevSeparator || isFirstVsible)
                {
                    separator.Visibility = Visibility.Collapsed;
                }
                else
                {
                    separator.Visibility = Visibility.Visible;
                }

                isPrevSeparator = true;
                isFirstVsible = false;
            }
            else
            {
                if ((containerFromItem as StatusBarItem)?.Visibility == Visibility.Visible)
                {
                    isPrevSeparator = false;
                    isFirstVsible = false;
                }
            }
        }
    }

    #endregion
}